﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SqlServer.Server;
using Ymatou.User.Dto.Auth;
using YmtAuth.AppService.Safe;
using YmtAuth.Common.Cache;
using YmtAuth.Common.Extend;
using YmtAuth.Common.Utility;
using YmtAuth.Domain.DomainEvent;
using YmtAuth.Domain.Model.Safe;
using YmtAuth.Domain.Shard;
using YmtAuth.Dto;
using YmtAuth.Repository.MSSQL;
using YmtAuth.Repository.MySQL;
using YmtAuth.Repository.YmtAuthDomainRepository;
using YmtSystem.CrossCutting;
using YmtSystem.Infrastructure.EventBusService;
using YmtAuth.Domain.Repository;
using YmtAuth.Domain.Model.LoginAuth;

namespace YmtAuth.AppService
{
    /// <summary>
    /// 用户安全验证
    /// </summary>
    public class UserSafeVerify
    {
        private static UserLoginAuthRepository repo = new UserLoginAuthRepository();
        private static LocalCache2<int, byte> userTypeCache = new LocalCache2<int, byte>(100000); 
        /// <summary>
        /// 注册安全处理流程
        /// </summary>
        public static void RegisterSafeHandleFlow()
        {
            AuthSafeHandleFactory.RegisterHandle();
            InitRepo();
        }

        public static IpAreaInfoAuthEntity.IpAreaInfo FindUserIpArea(string ip)
        {
            try
            {
                return repo.IpAreaRepo.FindIpAreaInfo(IpAreaInfoAuthEntity.IpConvertToLong(ip));
            }
            catch (Exception)
            {
                return null;
            }
            
        }

        /// <summary>
        /// 用户登录执行安全认证流程
        /// </summary>      
        public static AuthSafeHandleCode LoginAuthSafeHandleExecute(string loginid, int userid, string ip
            , string appSource, string deviceId,string appClientId)
        {          
            var requestId = HttpHelper.GetHeaderParameter(YmtUserKey.RequestId);

            YmatouLoggingService.Debug("[LoginAuthSafeHandleExecute] Begin，loginid {0}，userid {1}， requestId: {2}", loginid, userid, requestId);
            var par = new UserLoginParameter
            {
                LoginId = loginid,
                UserId = userid,
                AppClientId = appClientId,
                DeviceId = deviceId,
                Source = appSource,
                Ip = ip,
                RequestId = requestId
            };
            var cfg = new AuthSafeConfiguration
            {
                SafeCfg = SafeVerifyConfiguration.LoginSafeCfg.GetLoinSafeConfiguration() ,
                BlackCfg = SafeVerifyConfiguration.BlackListCfg.GetIpBlacklistConfiguration()
            };
            BlackListAppService.InitIpMatchpec();
            var stopWatch = Stopwatch.StartNew();
            var userType = userTypeCache.GetOrAdd(userid, uid => new LocalCache2<int, byte>._CacheItem()
            {
                Value = Convert.ToByte(RepositoryDependencies.UserRepository.Invoke().TryFindUserType(uid)),
                ExpiredTime = TimeSpan.FromDays(1)
            });
            var code = AuthSafeHandleFactory.AuthSafeHandleExecute(par, repo, cfg, userType);
            stopWatch.Stop();
            YmatouLoggingService.Debug("[LoginAuthSafeHandleExecute] Done，loginid {0}，userid {1}，result {2}，message {3}，run {4} ms， requestId: {5}", loginid, userid, code.Code, code.Message, stopWatch.ElapsedMilliseconds, requestId);
            return code;
        }
        /// <summary>
        /// 改变IP区域认证状态
        /// </summary>     
        public static ResponseData<LoginAuthResponseDto2> ChangeIpAreaAuthStatus(int userid, string loginid, bool success, string ip, string source, string ipAreaSource)
        {         
            YmtSystemAssert.AssertArgumentNotNull(ip, "ip can't null");
            if (!success)
            {
               //TODO 客户端验证失败
                EventBus.Instance.Publish(new LoginSafeVerifyEvent
                {
                    UserId = userid,
                    AppSource = source,
                    AuthType = "ChangeIpAreaAuthStatus",
                    ResponseCode = AuthSafeHandleCode.ClientAuthFail,
                    LoginId = loginid,
                    LoginIp = ip,
                    Desc = "客户端身份验证失败",
                });
               YmatouLoggingService.Error("user {0}, ip {1} 客户端 source {2} 身份认证失败", userid, ip, source);
               return ResponseData<LoginAuthResponseDto2>.CreateFail(
                    new LoginAuthResponseDto2 {Code = LoginResponseCode.NeedIdentityVerify}, errorCode: "100",
                    lastErrorMessage: "请执行身份验证");
            }
            var rawIpInfo = repo.IpAreaRepo.FindIpAreaInfo(IpAreaInfoAuthEntity.IpConvertToLong(ip));
            if (rawIpInfo == null)
            {
                //TODO 发布IP原始区域数据不存在事件
                EventBus.Instance.Publish(new LoginSafeVerifyEvent
                {
                    UserId = userid,
                    AppSource = source,
                    AuthType = "ChangeIpAreaAuthStatus",
                    ResponseCode = AuthSafeHandleCode.RawIpNotFind,
                    LoginId = loginid,
                    LoginIp = ip,
                    Desc = "IP原始数据不存在无法认证",
                });
                YmatouLoggingService.Error("user {0}, ip {1} raw ip not find,login source {2}", userid, ip,source);
                return ResponseData<LoginAuthResponseDto2>.CreateSuccess(new LoginAuthResponseDto2 { Code = LoginResponseCode.OK });
            }           
            int result = 0;
            //如果IP认证库不存在数据，则创建
            var ipAreaInfo = repo.IpAreaRepo.FindAreaInfoAuthEntity(userid, rawIpInfo.Country, rawIpInfo.Province, rawIpInfo.City);
            if(ipAreaInfo==null)           
            {
                ipAreaInfo = new IpAreaInfoAuthEntity(userid,rawIpInfo.Country, rawIpInfo.Province, rawIpInfo.City,ip,appSource: source);
                ipAreaInfo.SetAuthStatus(success, ipAreaSource);               
                result = repo.IpAreaRepo.TrySaveChange(ipAreaInfo);
            }
            else
            {
                ipAreaInfo.SetAuthStatus(success ? 200 : 0);
                ipAreaInfo.FlushSource(source);
                ipAreaInfo.FlushLoginIp(ip);
               result = repo.IpAreaRepo.TrySaveChange(ipAreaInfo);
            }
            var code = result > 0 ? AuthSafeHandleCode.IPAreaAuthSuccess : AuthSafeHandleCode.DbExecuteFail;
            var desc = result < 0 ? "MySQL保存用户IP区域认证失败" : "用户IP区域认证通过";
            //TODO 发布IP区域认证事件
            EventBus.Instance.Publish(new LoginSafeVerifyEvent
            {
                UserId = userid,
                AppSource = source,
                AuthType = "ChangeIpAreaAuthStatus",
                ResponseCode = code,
                LoginId = loginid,
                LoginIp = ip,
                Desc = desc,               
            });
            YmatouLoggingService.Debug("user {0},ip {1},authstatus {2},db save result {3}", userid, ip, ipAreaInfo.AuthStatus, result);
            return ResponseData<LoginAuthResponseDto2>.CreateSuccess(
                 new LoginAuthResponseDto2 { Code = LoginResponseCode.OK });
        }

        /// <summary>
        /// 改变常用设备认证状态
        /// </summary>       
        public static ResponseData<LoginAuthResponseDto2> ChangeDeviceAuthStatus(int userid, string loginid,
            bool success, string deviceId, string clientId, string ip, string source, string dataSource)
        {
            if (!success)
            {
                //TODO 客户端验证失败
                EventBus.Instance.Publish(new LoginSafeVerifyEvent
                {
                    UserId = userid,
                    AppSource = source,
                    AuthType = "ChangeDeviceAuthStatus",
                    ResponseCode = AuthSafeHandleCode.AppDeviceClientVerifyFail,
                    LoginId = loginid,
                    LoginIp = ip,
                    Desc = "客户端身份验证失败",
                    DeviceId = deviceId,
                    ClientId = clientId
                });
                YmatouLoggingService.Error("user {0}, ip {1} 客户端 source {2} 身份认证失败", userid, ip, source);
                return ResponseData<LoginAuthResponseDto2>.CreateFail(
                     new LoginAuthResponseDto2 { Code = LoginResponseCode.NeedIdentityVerify }, errorCode: "100",
                     lastErrorMessage: "请执行身份验证");                
            }
            int result = 0;
            UserDeviceAuthEntity device=null;
            if (!deviceId.IsEmpty())
            {
                device = repo.DeviceRepo.FindUserDevice(userid, deviceId, UserDeviceAuthEntity.DeviceIdType);
                if (device == null)
                {
                    device = new UserDeviceAuthEntity(userid, deviceId, source, dataSource, Convert.ToByte(UserDeviceAuthEntity.DeviceIdType));
                    device.SetDeviceAuthStatus(success);
                    result = repo.DeviceRepo.TrySaveChange(device);
                }
                else
                {
                    device.SetDeviceAuthStatus(success);
                    device.SetDesc(dataSource);
                    device.SetType(Convert.ToByte(UserDeviceAuthEntity.DeviceIdType));
                    result = repo.DeviceRepo.TrySaveChange(device);
                }
            }

            if (!clientId.IsEmpty())
            {
                device = repo.DeviceRepo.FindUserDevice(userid, clientId, UserDeviceAuthEntity.ClientIdType);
                if (device == null)
                {
                    device = new UserDeviceAuthEntity(userid, clientId, source, dataSource, Convert.ToByte(UserDeviceAuthEntity.ClientIdType));
                    device.SetDeviceAuthStatus(success);
                    result = repo.DeviceRepo.TrySaveChange(device);
                }
                else
                {
                    device.SetDeviceAuthStatus(success);
                    device.SetDesc(dataSource);
                    device.SetType(Convert.ToByte(UserDeviceAuthEntity.ClientIdType));
                    result = repo.DeviceRepo.TrySaveChange(device);
                }
            }
       
            var code = result > 0 ? AuthSafeHandleCode.AppDeviceAuthSuccess : AuthSafeHandleCode.DbExecuteFail;
            var desc = result < 0 ? "MySQL保存用户设备号认证失败" : "用户设备号认证通过";
            EventBus.Instance.Publish(new LoginSafeVerifyEvent
            {
                UserId = userid,
                AppSource = source,
                AuthType = "ChangeUserDeviceStatus",
                ResponseCode = code,
                LoginId = loginid,
                LoginIp = ip,
                Desc = desc,
                DeviceId = deviceId,
                ClientId = clientId
            });
            YmatouLoggingService.Debug("user {0},ip {1},authstatus {2},db save result {3}", userid, ip,device!=null? device.AuthStatus:0, result);
            return ResponseData<LoginAuthResponseDto2>.CreateSuccess(
                 new LoginAuthResponseDto2 { Code = LoginResponseCode.OK });
        }

        private static void AddUserIpAreaTask(string ip, int userId, string source, int status, string ipAreaSource)
        {
            Task.Factory.StartNew(() =>
            {
                long ipStart = 0L;
                if (!IpAreaInfoAuthEntity.IpConvertToLong(ip, out ipStart))
                {
                    YmatouLoggingService.Debug("AddUserIpAreaTask, invalid ip format, userId: {0}, ip: {1}, ipAreaSource: {2}", userId, ip, ipAreaSource);
                    using (var mm = Ymatou.PerfMonitorClient.MethodMonitor.New("auth_InvalidIP"))
                    {

                    }
                    return;
                }

                var rawIpArea = repo.IpAreaRepo.FindIpAreaInfo(ipStart);
                if (rawIpArea == null)
                {
                    YmatouLoggingService.Debug("IP区域原始数据不存在，无法收集用户登录IP区域数据");
                    return;
                }
                var ipArea = repo.IpAreaRepo.FindAreaInfoAuthEntity(userId, rawIpArea.Country, rawIpArea.Province,
                    rawIpArea.City);
                if (ipArea == null)
                {
                    ipArea = new IpAreaInfoAuthEntity(userId, rawIpArea.Country, rawIpArea.Province, rawIpArea.City, ip,
                        source, ipAreaSource:ipAreaSource);
                    if (status == 200)
                    {
                        ipArea.SetAuthStatus(true, ipAreaSource);
                    }                    
                    var result = repo.IpAreaRepo.Insert(ipArea, true);
                    YmatouLoggingService.Debug("[AddUserIpAreaTask] User {0}, {1} IPAREA {2}，{3} add ok,{4}", userId, ip, rawIpArea.Province,
                        rawIpArea.City, result);
                }
                else
                {
                    ipArea.FlushLastUpdateTime();
                    ipArea.FlushLoginIp(ip);                  
                    //ipArea.SetAuthStatus(status);

                    var result = repo.IpAreaRepo.SaveChange(ipArea);
                    YmatouLoggingService.Debug("[AddUserIpAreaTask] User {0},{1} IPAREA {2}，{3} update ok,{4}", userId, ip, rawIpArea.Province,
                       rawIpArea.City, result);
                }
            }).ContinueWith(task =>
            {
                if (task.Exception != null)
                    YmatouLoggingService.Error(
                string.Format("添加用户常用IP区域异常: userId: {0}, ip: {1}, source: {2}, status: {3}, ipAreaSource: {4}",
                            userId, ip, source, status, ipAreaSource), task.Exception.GetBaseException());
            }, TaskContinuationOptions.OnlyOnFaulted);                     
        }

        /// <summary>
        /// 收集用户登录常用IP区域
        /// </summary>     
        public static void AddUserLoginIpAreaAsync(string ip, int userId, string source,int status=0)
        {
            var isOpen = SafeVerifyConfiguration.LoginSafeCfg.GetLoinSafeConfiguration()
                    .TryGet(SafeCfgKey.LoginSafe_Pc_GetUserIpArea);
            if (isOpen.IsEmpty() || isOpen != "1") return;
            AddUserIpAreaTask(ip, userId, source, status, IPAreaSourceType.Login);
        }

        /// <summary>
        /// 认证收集用户常用IP区域
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="userId"></param>
        /// <param name="source"></param>
        /// <param name="status"></param>
        /// <param name="ipAreaSource"></param>
//        public static void AddTokenAuthIpAreaAsync(string ip, int userId, string source, int status, string ipAreaSource)
//        {
//            var isOpen = SafeVerifyConfiguration.LoginSafeCfg.GetLoinSafeConfiguration()
//                    .TryGet(SafeCfgKey.LoginSafe_AuthToken_GetUserIpArea);
//            if (isOpen.IsEmpty() || isOpen == "1")
//            {
//                AddUserIpAreaTask(ip, userId, source, status, ipAreaSource);
//            }
//        }

        /// <summary>
        /// 添加用户注册IP区域
        /// </summary>  
        public static void AddUserRegisterIpAreaAsync(string ip, int userId, string source, int status)
        {
            AddUserIpAreaTask(ip, userId, source, status, IPAreaSourceType.Register);
        }
        /// <summary>
        /// 添加用户注册设备号
        /// </summary> 
        public static void AddUserRegisterDeviceAsycn(string deviceId, string clientId, int userId, string loginId, string source, string ip, int status = 0)
        {
            AddUserDeviceTask(deviceId, clientId, userId, loginId, source, ip, status, CONSTANT.UserDeviceAuth_DataSource_Register);
        }
        /// <summary>
        /// 添加用户登录设备号
        /// </summary> 
        public static void AddUserLoginDeviceAsync(string deviceId, string clientId, int userId, string loginId,
            string source, string ip, int status = 0)
        {
            var isOpen = SafeVerifyConfiguration.LoginSafeCfg.GetLoinSafeConfiguration()
                   .TryGet(SafeCfgKey.LoginSafe_App_GetUserDevice);
            if (isOpen.IsEmpty() || isOpen != "1") return;
            AddUserDeviceTask(deviceId, clientId, userId, loginId, source, ip, status, CONSTANT.UserDeviceAuth_DataSource_Login);
        }

        public static int GetBlackActionCode(string source)
        {
             var code =
                    SafeVerifyConfiguration.LoginSafeCfg.GetLoinSafeConfiguration().TryGet(SafeCfgKey.LoginSafe_PcBlacklist_BlacklistAction, "100")
                        .ConvertToInt32(AuthSafeHandleCode.ContinueNextVerify);
            if(!string.IsNullOrEmpty(source)&& source.ToLower()=="app")
                code =
                    SafeVerifyConfiguration.LoginSafeCfg.GetLoinSafeConfiguration().TryGet(SafeCfgKey.LoginSafe_AppIpBlacklist_BlacklistAction, "100")
                        .ConvertToInt32(AuthSafeHandleCode.ContinueNextVerify);
            return code;
        }

        /// <summary>
        /// 添加用户常用设备
        /// </summary>      
        private static void AddUserDeviceTask(string deviceId, string clientId, int userId, string loginId, string source, string ip, int status = 0, string dataSource = "auto insert")
        {
            //非APP来源不收集设备号
            if (source.IsEmpty() || source.ToLower()!="app")
                return;
            Task.Factory.StartNew(() =>
            {               
                var key = deviceId.IsEmpty() ? clientId : deviceId;
                if (key.IsEmpty())
                {
                    EventBus.Instance.Publish(new LoginSafeVerifyEvent
                    {
                        UserId = userId,
                        AppSource = source,
                        AuthType = "AutoAddUserDevice",
                        ResponseCode = AuthSafeHandleCode.UserDeviceIsEmpty,
                        LoginId = loginId,
                        LoginIp = ip,
                        Desc = "用户常用设备为空无法收集数据",
                        RunTime = 0
                    });
                    YmatouLoggingService.Debug("用户,userID:{0},loginId:{1}，设备号，ClientId 为空无法收集数据",userId,loginId);
                    return;
                }
                //FIX BUG: ymatouusersafe.userdeviceauth; 0 - deviceId; 1 - clientId
                var by = deviceId.IsEmpty() ? UserDeviceAuthEntity.ClientIdType : UserDeviceAuthEntity.DeviceIdType;

                var deviceInfo = repo.DeviceRepo.FindUserDevice(userId, key, by);
                if (deviceInfo == null)
                {
                    deviceInfo = new UserDeviceAuthEntity(userId, key, source, dataSource, by);
                    if(status==200)
                        deviceInfo.SetDeviceAuthStatus(true);
                    else
                       deviceInfo.SetDeviceAuthInitStatus();
                    var result = repo.DeviceRepo.Insert(deviceInfo,true);
                    YmatouLoggingService.Debug("[AddUserDeviceTask] User {0} device {1} {2} add ok,{3}", userId, deviceId, clientId, result);
                }
                else
                {
                   deviceInfo.FlushLastUpdateTime();
                   var result = repo.DeviceRepo.SaveChange(deviceInfo);
                   YmatouLoggingService.Debug("[AddUserDeviceTask] User {0} device {1} {2} update ok,{3}", userId, deviceId, clientId, result);
                }
            })
            .ContinueWith(task => {
                                        if (task.Exception != null)
                                            YmatouLoggingService.Error("添加用户常用设备异常 ", task.Exception.InnerException);
            },
                    TaskContinuationOptions.OnlyOnFaulted);
        }
        
        /// <summary>
        /// 清理所有安全处理流程
        /// </summary>
        public static void Clear()
        {
           AuthSafeHandleFactory.Clear();
        }
        //初始化仓储
        private static void InitRepo()
        {
            repo = new UserLoginAuthRepository
            {
                LoginHistoryRepo = new YmtUserLoginHistoryRepository(),
                IpAreaRepo = new IpAreaInfoRepository(),
                DeviceRepo = new UserDeviceAuthRepository(),
                IpBlackListRepo = new BulackListRepository(),
                DeviceTokenRepo = new DeviceTokenAuthRepository(),
                IpAreaTokenRepo = new IpAreaTokenRepository()
            };
        }
    }
}
